package org.vaadin.hezamu.googlemapwidget.widgetset.client.ui; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.google.gwt.http.client.Request; import com.google.gwt.http.client.RequestBuilder; import com.google.gwt.http.client.RequestCallback; import com.google.gwt.http.client.RequestException; import com.google.gwt.http.client.RequestTimeoutException; import com.google.gwt.http.client.Response; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONBoolean; import com.google.gwt.json.client.JSONNumber; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.json.client.JSONString; import com.google.gwt.json.client.JSONValue; import com.google.gwt.maps.client.Copyright; import com.google.gwt.maps.client.CopyrightCollection; import com.google.gwt.maps.client.InfoWindowContent; import com.google.gwt.maps.client.MapType; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.TileLayer; import com.google.gwt.maps.client.control.Control; import com.google.gwt.maps.client.control.HierarchicalMapTypeControl; import com.google.gwt.maps.client.control.LargeMapControl; import com.google.gwt.maps.client.control.MapTypeControl; import com.google.gwt.maps.client.control.MenuMapTypeControl; import com.google.gwt.maps.client.control.OverviewMapControl; import com.google.gwt.maps.client.control.ScaleControl; import com.google.gwt.maps.client.control.SmallMapControl; import com.google.gwt.maps.client.control.SmallZoomControl; import com.google.gwt.maps.client.event.MapClickHandler; import com.google.gwt.maps.client.event.MapMoveEndHandler; import com.google.gwt.maps.client.event.MarkerClickHandler; import com.google.gwt.maps.client.event.MarkerDragEndHandler; import com.google.gwt.maps.client.geom.LatLng; import com.google.gwt.maps.client.geom.LatLngBounds; import com.google.gwt.maps.client.geom.MercatorProjection; import com.google.gwt.maps.client.geom.Point; import com.google.gwt.maps.client.overlay.Icon; import com.google.gwt.maps.client.overlay.Marker; import com.google.gwt.maps.client.overlay.MarkerOptions; import com.google.gwt.maps.client.overlay.Overlay; import com.google.gwt.maps.client.overlay.Polygon; import com.google.gwt.maps.client.overlay.PolygonOptions; import com.google.gwt.maps.client.overlay.Polyline; import com.google.gwt.maps.client.overlay.PolylineOptions; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; /** * Client side widget which communicates with the server. Messages from the * server are shown as HTML and mouse clicks are sent to the server. */ public class VGoogleMap extends Composite implements Paintable, MapClickHandler, MapMoveEndHandler, MarkerDragEndHandler { /** Set the CSS class name to allow styling. */ public static final String CLASSNAME = "v-googlemap"; public static final String CLICK_EVENT_IDENTIFIER = "click"; private static Boolean loadingApi = false; /** The client side widget identifier */ protected String paintableId; /** Reference to the server connection object. */ protected ApplicationConnection client; private MapWidget map = null; private final Map<String, Marker> knownMarkers = new HashMap<String, Marker>(); private final Map<Integer, Overlay> knownPolygons = new HashMap<Integer, Overlay>(); private boolean ignoreVariableChanges = true; private long markerRequestSentAt; private final List<MapControl> controls = new ArrayList<MapControl>(); private ArrayList<UIDL> uidl = new ArrayList<UIDL>(); private final SimplePanel wrapperPanel; private String apiKey = ""; private int logLevel = 0; private Timer apiLoadWaitTimer = null; public enum MapControl { SmallMapControl, HierarchicalMapTypeControl, LargeMapControl, MapTypeControl, MenuMapTypeControl, OverviewMapControl, ScaleControl, SmallZoomControl } class CustomTileLayer extends TileLayer { private final String tileUrl; private final boolean isPng; private final double opacity; public CustomTileLayer(CopyrightCollection copyColl, int minZoom, int maxZoom, String tileUrl, boolean isPng, double opacity) { super(copyColl, minZoom, maxZoom); this.tileUrl = tileUrl; this.isPng = isPng; this.opacity = opacity; } @Override public boolean isPng() { return isPng; } @Override public double getOpacity() { return opacity; } @Override public String getTileURL(Point tile, int zoomLevel) { String url = tileUrl.replace("{X}", "" + tile.getX()) .replace("{Y}", "" + tile.getY()) .replace("{ZOOM}", "" + zoomLevel); return url; } } /** * Load the GoogleMaps API asynchronously with the provided key. * * @param apiKey * - the API-key to be used. */ private static native void loadScript(String apiKey) /*-{ var script = $doc.createElement('script'); script.lang = "javascript"; script.src = "http://maps.google.com/maps?gwt=1&file=api&key="+apiKey+"&v=2.x&async=2"; $wnd.document.body.appendChild(script); }-*/; /** * Check for the existence of the GoogleMaps API, if not found the API is * loaded. * * @param apiKey * - the key to be used if API not loaded * @return true - if API loaded <br /> * false - if no API found */ private static boolean checkForGoogleMapApi(String apiKey) { if (com.google.gwt.maps.client.Maps.isLoaded()) { synchronized (loadingApi) { if (loadingApi) { loadingApi = false; } } return true; } else { synchronized (loadingApi) { if (!loadingApi) { loadingApi = true; loadScript(apiKey); } } return false; } } /** * Once the API has been loaded, the MapWidget can be initialized. This * method will initialize the MapWidget and place it inside the wrapper from * the composite root. */ private void loadMap() { map = new MapWidget(); wrapperPanel.add(map); // This method call of the Paintable interface sets the component // style name in DOM tree setStyleName(CLASSNAME); map.addMapMoveEndHandler(this); map.addMapClickHandler(this); // Update all the uidl requests that have been made if (uidl != null && uidl.size() > 0) { for (UIDL uidl : this.uidl) { updateFromUIDL(uidl, client); } } uidl = null; } /** * The constructor should first call super() to initialize the component and * then handle any initialization relevant to Vaadin. */ public VGoogleMap() { wrapperPanel = new SimplePanel(); initWidget(wrapperPanel); // All Composites need to call initWidget() } /** * Called whenever an update is received from the server */ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { String width = uidl.getStringAttribute("width"); String height = uidl.getStringAttribute("height"); logLevel = uidl.getIntAttribute("loglevel"); if (height != null) { wrapperPanel.setHeight(height); } if (width != null) { wrapperPanel.setWidth(width); } // Save reference to server connection object to be able to send // user interaction later this.client = client; if (map == null) { apiKey = uidl.getStringAttribute("apikey"); if (checkForGoogleMapApi(apiKey)) { loadMap(); } else { if (apiLoadWaitTimer == null) { apiLoadWaitTimer = new Timer() { @Override public void run() { if (checkForGoogleMapApi(apiKey)) { loadMap(); } else { schedule(100); } } }; apiLoadWaitTimer.schedule(100); } if (this.uidl == null) { ApplicationConnection .getConsole() .error("The ArrayList holding UIDL updates was NULL, this should never happen!"); this.uidl = new ArrayList<UIDL>(); } this.uidl.add(uidl); return; } } if (apiLoadWaitTimer != null) { apiLoadWaitTimer.cancel(); apiLoadWaitTimer = null; } if (client.updateComponent(this, uidl, true)) { return; } // Save the client side identifier (paintable id) for the widget paintableId = uidl.getId(); if (uidl.hasAttribute("cached") && uidl.getBooleanAttribute("cached")) { return; } long start = System.currentTimeMillis(); // Do not send any variable changes while changing the map ignoreVariableChanges = true; int newZoom = uidl.getIntVariable("zoom"); if (map.getZoomLevel() != newZoom) { map.setZoomLevel(newZoom); } LatLng newCenter = LatLng.newInstance( uidl.getDoubleVariable("center_lat"), uidl.getDoubleVariable("center_lng")); boolean scrollWheelZoomEnabled = uidl.getBooleanVariable("swze"); if (map.isScrollWheelZoomEnabled() != scrollWheelZoomEnabled) { map.setScrollWheelZoomEnabled(scrollWheelZoomEnabled); } if (map.getCenter().getLatitude() != newCenter.getLatitude() || map.getCenter().getLongitude() != newCenter.getLongitude()) { map.setCenter(newCenter); } for (MapControl control : MapControl.values()) { if (uidl.hasAttribute(control.name())) { if (!controls.contains(control)) { map.addControl(newControl(control)); controls.add(control); } } else if (controls.contains(control)) { map.removeControl(newControl(control)); controls.add(control); } } if (uidl.hasAttribute("markerRes")) { String markerUrl = client.translateVaadinUri(uidl .getStringAttribute("markerRes")); if (markerUrl != null) { DeferredCommand .addCommand(new MarkerRetrieveCommand(markerUrl)); } } if (uidl.hasAttribute("marker")) { // When adding the markers we get the ID from JSONString.toString() // which includes quotation marks around the ID. String markerId = "\"" + uidl.getStringAttribute("marker") + "\""; Marker marker = knownMarkers.get(markerId); for (final Iterator<Object> it = uidl.getChildIterator(); it .hasNext();) { final UIDL u = (UIDL) it.next(); if (!u.getTag().equals("tabs")) { continue; } if (u.getChildCount() == 0) { log(1, "No contents for info window"); } else if (u.getChildCount() == 1) { // Only one component in the info window -> no tabbing UIDL paintableUIDL = u.getChildUIDL(0).getChildUIDL(0); Paintable paintable = client.getPaintable(paintableUIDL); map.getInfoWindow().open(marker.getLatLng(), new InfoWindowContent((Widget) paintable)); // Update components in the info window after adding them to // DOM so that size calculations can succeed paintable.updateFromUIDL(paintableUIDL, client); } else { int tabs = u.getChildCount(); // More than one component, show them in info window tabs InfoWindowContent.InfoWindowTab[] infoTabs = new InfoWindowContent.InfoWindowTab[tabs]; Paintable[] paintables = new Paintable[tabs]; UIDL[] uidls = new UIDL[tabs]; int selectedId = 0; for (int i = 0; i < u.getChildCount(); i++) { UIDL childUIDL = u.getChildUIDL(i); if (selectedId == 0 && childUIDL.getBooleanAttribute("selected")) { selectedId = i; } String label = childUIDL.getStringAttribute("label"); UIDL paintableUIDL = childUIDL.getChildUIDL(0); Paintable paintable = client .getPaintable(paintableUIDL); paintables[i] = paintable; uidls[i] = paintableUIDL; infoTabs[i] = new InfoWindowContent.InfoWindowTab( label, (Widget) paintable); } map.getInfoWindow().open(marker.getLatLng(), new InfoWindowContent(infoTabs, selectedId)); // Update paintables after adding them to DOM so that // size calculations can succeed for (int i = 0; i < paintables.length; i++) { paintables[i].updateFromUIDL(uidls[i], client); } } } } if (uidl.hasAttribute("clearMapTypes")) { for (MapType type : map.getMapTypes()) { map.removeMapType(type); } } Map<Integer, Overlay> newPolys = new HashMap<Integer, Overlay>(); // Process polygon/polyline overlays and map types for (final Iterator<Object> it = uidl.getChildIterator(); it.hasNext();) { final UIDL u = (UIDL) it.next(); if (u.getTag().equals("overlays")) { long nodeStart = System.currentTimeMillis(); for (final Iterator<Object> iter = u.getChildIterator(); iter .hasNext();) { final UIDL polyUIDL = (UIDL) iter.next(); Overlay poly = null; if (polyUIDL.hasAttribute("fillcolor")) { poly = polygonFromUIDL(polyUIDL); } else { poly = polylineFromUIDL(polyUIDL); } if (poly != null) { newPolys.put(polyUIDL.getIntAttribute("id"), poly); } } log(1, "Polygon overlays processed in " + (System.currentTimeMillis() - nodeStart) + "ms"); } else if (u.getTag().equals("mapTypes")) { long nodeStart = System.currentTimeMillis(); for (final Iterator<Object> iter = u.getChildIterator(); iter .hasNext();) { map.addMapType(mapTypeFromUIDL((UIDL) iter.next())); } log(1, "Map types processed in " + (System.currentTimeMillis() - nodeStart) + "ms"); } } // Remove deleted overlays from the map List<Integer> removedPolyIds = new ArrayList<Integer>(); for (Entry<Integer, Overlay> entry : knownPolygons.entrySet()) { if (!newPolys.containsKey(entry.getKey())) { map.removeOverlay(entry.getValue()); removedPolyIds.add(entry.getKey()); } } // ... and from the map. Can't remove them while iterating the // collection for (Integer id : removedPolyIds) knownPolygons.remove(id); // Add new overlays for (Entry<Integer, Overlay> entry : newPolys.entrySet()) { if (!knownPolygons.containsKey(entry.getKey())) { knownPolygons.put(entry.getKey(), entry.getValue()); map.addOverlay(entry.getValue()); } } if (uidl.hasAttribute("closeInfoWindow")) { map.getInfoWindow().close(); } ignoreVariableChanges = false; if (uidl.hasAttribute("reportBounds") && uidl.getBooleanAttribute("reportBounds") == true) { reportMapBounds(); } log(1, "IGoogleMap.updateFromUIDL() took " + (System.currentTimeMillis() - start) + "ms"); } private MapType mapTypeFromUIDL(UIDL maptypeUIDL) { int minZoom = maptypeUIDL.getIntAttribute("minZoom"); int maxZoom = maptypeUIDL.getIntAttribute("maxZoom"); String copyright = maptypeUIDL.getStringAttribute("copyright"); String name = maptypeUIDL.getStringAttribute("name"); String tileUrl = maptypeUIDL.getStringAttribute("tileUrl"); boolean isPng = maptypeUIDL.getBooleanAttribute("isPng"); double opacity = maptypeUIDL.getDoubleAttribute("opacity"); CopyrightCollection myCopyright = new CopyrightCollection(); myCopyright.addCopyright(new Copyright(1, LatLngBounds.newInstance( LatLng.newInstance(-90, -180), LatLng.newInstance(90, 180)), minZoom, copyright)); return new MapType(new TileLayer[] { new CustomTileLayer(myCopyright, minZoom, maxZoom, tileUrl, isPng, opacity) }, new MercatorProjection(maxZoom - minZoom + 1), name); } private Control newControl(MapControl control) { if (control.equals(MapControl.SmallMapControl)) { return new SmallMapControl(); } if (control.equals(MapControl.HierarchicalMapTypeControl)) { return new HierarchicalMapTypeControl(); } if (control.equals(MapControl.LargeMapControl)) { return new LargeMapControl(); } if (control.equals(MapControl.MapTypeControl)) { return new MapTypeControl(); } if (control.equals(MapControl.MenuMapTypeControl)) { return new MenuMapTypeControl(); } if (control.equals(MapControl.OverviewMapControl)) { return new OverviewMapControl(); } if (control.equals(MapControl.ScaleControl)) { return new ScaleControl(); } if (control.equals(MapControl.SmallZoomControl)) { return new SmallZoomControl(); } log(1, "Unknown control: " + control); return null; } private Polyline polylineFromUIDL(UIDL polyUIDL) { String[] encodedPoints = polyUIDL.getStringAttribute("points").split( " "); LatLng[] points = new LatLng[encodedPoints.length]; for (int i = 0; i < encodedPoints.length; i++) { String[] p = encodedPoints[i].split(","); double lat = Double.parseDouble(p[0]); double lng = Double.parseDouble(p[1]); points[i] = LatLng.newInstance(lat, lng); } String color = polyUIDL.getStringAttribute("color"); int weight = polyUIDL.getIntAttribute("weight"); double opacity = polyUIDL.getDoubleAttribute("opacity"); boolean clickable = polyUIDL.getBooleanAttribute("clickable"); return new Polyline(points, color, weight, opacity, PolylineOptions.newInstance(clickable, false)); } private Polygon polygonFromUIDL(UIDL polyUIDL) { String[] encodedPoints = polyUIDL.getStringAttribute("points").split( " "); LatLng[] points = new LatLng[encodedPoints.length]; for (int i = 0; i < encodedPoints.length; i++) { String[] p = encodedPoints[i].split(","); double lat = Double.parseDouble(p[0]); double lng = Double.parseDouble(p[1]); points[i] = LatLng.newInstance(lat, lng); } String color = polyUIDL.getStringAttribute("color"); int weight = polyUIDL.getIntAttribute("weight"); double opacity = polyUIDL.getDoubleAttribute("opacity"); String fillColor = polyUIDL.getStringAttribute("fillcolor"); double fillOpacity = polyUIDL.getDoubleAttribute("fillopacity"); boolean clickable = polyUIDL.getBooleanAttribute("clickable"); return new Polygon(points, color, weight, opacity, fillColor, fillOpacity, PolygonOptions.newInstance(clickable)); } private Marker createMarker(JSONNumber jsLat, JSONNumber jsLng, JSONString jsTitle, JSONBoolean jsVisible, JSONString jsIcon, int iconAnchorX, int iconAnchorY, JSONBoolean jsDraggable) { Icon icon = null; if (jsIcon != null) { icon = Icon.newInstance(jsIcon.stringValue()); icon.setIconAnchor(Point.newInstance(iconAnchorX, iconAnchorY)); log(1, "Icon URL '" + jsIcon.stringValue() + "' at anchor point (" + iconAnchorX + "," + iconAnchorY + ")"); } MarkerOptions mopts; if (icon != null) { mopts = MarkerOptions.newInstance(icon); } else { mopts = MarkerOptions.newInstance(); } mopts.setTitle(jsTitle.stringValue()); mopts.setDraggable(jsDraggable.booleanValue()); final double lat = jsLat.doubleValue(); final double lng = jsLng.doubleValue(); if (lat < -90 || lat > 90) { log(1, "Invalid latitude for marker: " + lat); return null; } if (lng < -180 || lng > 180) { log(1, "Invalid latitude for marker: " + lat); return null; } Marker result = new Marker(LatLng.newInstance(lat, lng), mopts); result.setVisible(jsVisible.booleanValue()); return result; } private String getMarkerIconURL(Marker marker) { if (marker.getIcon() == null) return null; return marker.getIcon().getImageURL(); } public void onClick(MapClickEvent event) { if (ignoreVariableChanges) { return; } if (event.getOverlay() != null) { return; } client.updateVariable(paintableId, "click_pos", event.getLatLng() .toString(), true); } public void onMoveEnd(MapMoveEndEvent event) { if (ignoreVariableChanges) { return; } reportMapBounds(); } private void reportMapBounds() { client.updateVariable(paintableId, "zoom", map.getZoomLevel(), false); client.updateVariable(paintableId, "bounds_ne", map.getBounds() .getNorthEast().toString(), false); client.updateVariable(paintableId, "bounds_sw", map.getBounds() .getSouthWest().toString(), false); client.updateVariable(paintableId, "center", map.getCenter().toString(), true); } public void onDragEnd(MarkerDragEndEvent event) { Marker marker = (Marker) event.getSource(); Set<String> keys = knownMarkers.keySet(); for (String key : keys) { // Find the key for the moved marker if (knownMarkers.get(key).equals(marker)) { client.updateVariable(paintableId, "markerMovedId", key, false); client.updateVariable(paintableId, "markerMovedLat", marker .getLatLng().getLatitude(), false); client.updateVariable(paintableId, "markerMovedLong", marker .getLatLng().getLongitude(), true); break; } } } protected void markerClicked(String mId) { client.updateVariable(paintableId, "marker", mId, true); } private void log(int level, String message) { if (level <= logLevel) { // Show message in GWT console System.out.println(message); // And also in Vaadin debug window ApplicationConnection.getConsole().log(message); } } class InfoWindowOpener implements MarkerClickHandler { private final String markerId; InfoWindowOpener(String markerId) { super(); this.markerId = markerId; } public void onClick(MarkerClickEvent event) { markerClicked(markerId); } } class MarkerRetrieveCommand implements Command { private final String markerUrl; MarkerRetrieveCommand(String markerUrl) { super(); this.markerUrl = markerUrl; } public void execute() { RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, markerUrl); try { builder.setTimeoutMillis(2000); markerRequestSentAt = System.currentTimeMillis(); builder.sendRequest(null, new RequestCallback() { public void onError(Request request, Throwable e) { if (e instanceof RequestTimeoutException) { log(1, "Timeout fetching marker data: " + e.getMessage()); } else { log(1, "Error fetching marker data: " + e.getMessage()); } } public void onResponseReceived(Request request, Response response) { String markerJSON = response.getText(); System.out.println("" + markerJSON.length() + " bytes of marker response got in " + (System.currentTimeMillis() - markerRequestSentAt) + "ms"); JSONArray array = null; try { long start = System.currentTimeMillis(); JSONValue json = JSONParser.parse(markerJSON); array = json.isArray(); log(1, "JSON parsed in " + (System.currentTimeMillis() - start) + "ms"); if (array == null) { System.out .println("Marker JSON was not an array."); return; } handleMarkerJSON(array); } catch (Exception e) { log(1, "Error parsing json: " + e.getMessage()); } } }); } catch (RequestException e) { log(1, "Failed to send the request: " + e.getMessage()); } } private void handleMarkerJSON(JSONArray array) { synchronized (knownMarkers) { JSONValue value; long startTime = System.currentTimeMillis(); int initSize = knownMarkers.size(); List<String> markersFromThisUpdate = new ArrayList<String>(); for (int i = 0; i < array.size(); i++) { JSONObject jsMarker; JSONString jsMID, jsTitle, jsIcon; JSONNumber jsLat, jsLng; JSONBoolean jsVisible, jsHasInfo, jsDraggable; Marker marker = null; boolean isOldMarker = false; boolean replaceMarker = false; if ((jsMarker = array.get(i).isObject()) == null) { continue; } // Read marker id if ((value = jsMarker.get("mid")) == null) { continue; } if ((jsMID = value.isString()) == null) { continue; } if ((value = jsMarker.get("draggable")) == null) { continue; } else { if (knownMarkers.containsKey(jsMID.toString())) { marker = knownMarkers.get(jsMID.toString()); marker.setDraggingEnabled((((JSONBoolean) jsMarker .get("draggable")).booleanValue())); isOldMarker = true; } } // Add maker to list of markers in this update markersFromThisUpdate.add(jsMID.toString()); // Read marker latitude if ((value = jsMarker.get("lat")) == null) { if (!isOldMarker) continue; } if ((jsLat = value.isNumber()) == null) { if (!isOldMarker) continue; } // Read marker longitude if ((value = jsMarker.get("lng")) == null) { if (!isOldMarker) continue; } if ((jsLng = value.isNumber()) == null) { if (!isOldMarker) continue; } else { // marker.setLatLng(jsLng.doubleValue()); } // Read marker title if ((value = jsMarker.get("title")) == null) { if (!isOldMarker) continue; } if ((jsTitle = value.isString()) == null) { if (!isOldMarker) continue; } else { if (isOldMarker && marker != null) { String title = marker.getTitle(); // if title is changed if (!jsTitle.stringValue().equals(title)) { replaceMarker = true; log(1, "Title changed: " + marker.getTitle()); } } } // Read marker visibility if ((value = jsMarker.get("visible")) == null) { if (!isOldMarker) continue; } if ((jsVisible = value.isBoolean()) == null) { if (!isOldMarker) continue; } else { if (marker != null) { boolean old = marker.isVisible(); marker.setVisible(jsVisible.booleanValue()); if (old != marker.isVisible()) { log(1, "Toggled marker '" + marker.getTitle() + "' visibility to " + jsVisible.booleanValue()); } } } // Read marker draggability (is that a word? :) if ((value = jsMarker.get("draggable")) == null) { if (!isOldMarker) continue; } if ((jsDraggable = value.isBoolean()) == null) { if (!isOldMarker) continue; } // Change position, if changed if (marker != null && jsLat != null && jsLng != null && marker.getLatLng() != null) { LatLng llang = marker.getLatLng(); LatLng llang2 = LatLng.newInstance(jsLat.doubleValue(), jsLng.doubleValue()); if (!llang.isEquals(llang2)) { marker.setLatLng(llang2); } } // Read marker icon if ((value = jsMarker.get("icon")) == null) { jsIcon = null; if (marker != null) { String currentURL = getMarkerIconURL(marker); if (!currentURL .startsWith("http://maps.gstatic.com") && currentURL != null && currentURL != "") { replaceMarker = true; log(1, "Icon url changed " + marker.getTitle() + " from '" + currentURL + "'"); } } } else if ((jsIcon = value.isString()) == null) { if (!isOldMarker) continue; } else { if (marker != null && getMarkerIconURL(marker) != jsIcon .toString()) { replaceMarker = true; log(1, "Icon url changed 2 " + marker.getTitle()); } } int iconAnchorX = 0; if ((value = jsMarker.get("iconAnchorX")) != null) { JSONNumber jsAnchorX; if ((jsAnchorX = value.isNumber()) != null) { log(1, "Anchor X: " + jsAnchorX.toString()); iconAnchorX = (int) Math.round(jsAnchorX .doubleValue()); } else { log(1, "Anchor X NaN"); } } int iconAnchorY = 0; if ((value = jsMarker.get("iconAnchorY")) != null) { JSONNumber jsAnchorY; if ((jsAnchorY = value.isNumber()) != null) { iconAnchorY = (int) Math.round(jsAnchorY .doubleValue()); } } // do not create new one if old found (only if we want to // replace it) if (isOldMarker && !replaceMarker) continue; if (!isOldMarker) replaceMarker = false; // Never replace a marker if // there is no previous one if (replaceMarker) { log(1, "Replacing marker " + marker.getTitle()); map.removeOverlay(marker); markersFromThisUpdate.remove(marker); } marker = createMarker(jsLat, jsLng, jsTitle, jsVisible, jsIcon, iconAnchorX, iconAnchorY, jsDraggable); if (marker != null) { map.addOverlay(marker); // Add dragEnd handlers to marker marker.addMarkerDragEndHandler(VGoogleMap.this); // Read boolean telling if marker has a info window if ((value = jsMarker.get("info")) != null) { if ((jsHasInfo = value.isBoolean()) != null && jsHasInfo.booleanValue()) { marker.addMarkerClickHandler(new InfoWindowOpener( jsMID.stringValue())); } } knownMarkers.put(jsMID.toString(), marker); } } int newMarkers = knownMarkers.size() - initSize; long dur = System.currentTimeMillis() - startTime; if (newMarkers == 0) { log(1, "No new markers added in " + dur + "ms."); } else { log(1, "" + newMarkers + " markers added in " + dur + "ms: " + dur / newMarkers + "ms per marker"); } // Remove markers that wasn't in the update (i.e. removed on // server side) List<String> removedMarkers = new ArrayList<String>(); for (String mID : knownMarkers.keySet()) { if (!markersFromThisUpdate.contains(mID)) { map.removeOverlay(knownMarkers.get(mID)); removedMarkers.add(mID); } } for (String mID : removedMarkers) { knownMarkers.remove(mID); } } } } @Override public void setHeight(String height) { super.setHeight(height); wrapperPanel.setHeight(height); if (map != null) { map.setHeight(height); } else { ApplicationConnection.getConsole().error( "Set height attempted before map initialized"); } } @Override public void setWidth(String width) { super.setWidth(width); wrapperPanel.setWidth(width); if (map != null) { map.setWidth(width); } else { ApplicationConnection.getConsole().error( "Set width attempted before map initialized"); } } }